0.前端部分依然基于VueCLI (https://cli.vuejs.org/zh/)
1.创建hello-login文件夹,然后再此文件夹内执行 vue create front-end ,一顿狂回车后,如下图所示:
![](https://img2020.cnblogs.com/blog/78238/202007/78238-20200717155841577-1173915616.png)
2.安装elementUI,axios,js-cookie,qs 2.1 npm i element-ui -S (https://element.eleme.cn/#/zh-CN/component/installation) 2.2 npm install --save axios vue-axios (http://www.axios-js.com/zh-cn/docs/vue-axios.html) 2.3 npm install js-cookie --save (https://www.npmjs.com/package/js-cookie) 2.4 npm install qs --save (https://www.npmjs.com/package/qs)
3.打开main.js,把elementUI和axios加载。搞定这块代码,npm run serve,试试能否正常把项目跑起来。(这是一种编码方式,安装组件算是破坏性的动作,需要勤于测试。以免后期跪了)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import axios from 'axios'
import VueAxios from 'vue-axios'
//Vue.config.productionTip = false
axios.defaults.withCredentials = true
Vue.use(VueAxios,axios)
Vue.use(ElementUI)
new Vue({
render: h => h(App),
}).$mount('#app')
View Code
4.在components目录下 创建Login.vue文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{{ titleMsg }}
提交
重置
export default {
name: 'loginForm',
data() {
return {
titleMsg: '欢迎来到旗帜世界',
loginData: {
username: '',
password: ''
}
}
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
console.log('submit')
} else {
console.log('illegad submit!!');
return false;
}
})
},
resetForm(formName) {
this.$refs[formName].resetFields()
console.log('reset')
}
}
}
View Code
5.打开App.vue,将helloworld相关代码注释,改写成Login。这种操作可以加深理解vue的组件机制。为后期学习使用router打基础
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// import HelloWorld from './components/HelloWorld.vue'
import Login from './components/Login.vue'
export default {
name: 'App',
components: {
//HelloWorld
Login
}
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
View Code
6.npm run serve 跑起来后如下图。![](https://img2020.cnblogs.com/blog/78238/202007/78238-20200717164520085-1548734615.png)
7.接下来,使用axios把此表单提交到py搭建的后台并返回消息。先转到py端,把后端代码实现一部分。在完成这个功能吧。
--------------------------------------------------------------------------------------------------------------------------------------------------------------
0.后端实现,使用flask(https://dormousehole.readthedocs.io/en/latest/)
1.在hello-login文件夹下创建 back-end文件夹,并运行命令行 py -m venv venv 这时就会在当前目录下创建venv虚拟机 (https://dormousehole.readthedocs.io/en/latest/installation.html#id4)
2.执行venv\scripts\activate 这样就启动了虚拟机环境 ![](https://img2020.cnblogs.com/blog/78238/202007/78238-20200717183815215-748226606.png)
3.开始安装flask, flask-login, flask-cors, jwt 。注意都要在venv虚拟机环境下安装 3.1 pip install Flask (https://dormousehole.readthedocs.io/en/latest/installation.html#flask) 3.2 pip install flask-login (https://flask-login.readthedocs.io/en/latest/) 3.3 pip install pyjwt (https://pypi.org/project/PyJWT/) 3.4 pip install -U flask-cors (https://flask-cors.readthedocs.io/en/latest/)
4.在当前目录下创建app.py文件 敲入代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask import Flask
import json
app = Flask(__name__)
app.secret_key =b'\x15f\x07\xd3\xd9\xbf*\x82\xd1\xe6\xb4\xf2\x95\xdd\x8f\x12'
#命令行中运行后拷贝出随机值 python -c "import os; print(os.urandom(16))"
@app.route('/hello')
def helloworld():
returnData = {'code': 0, 'msg': 'success', 'data': 'hello world' }
return json.dumps(returnData),200
if __name__ == '__main__':
app.run(debug = True)
View Code
5.在venv虚拟机下 运行py app.py 然后再浏览器中查看 http://localhost:5000/hello 。这说明基本框架已经构建成功。之所以返回如下格式,是参考了这篇博文(https://sobird.me/http-json-api-guide.htm),原始出处并未找到,![](https://img2020.cnblogs.com/blog/78238/202007/78238-20200717190631140-1724407833.png)
6.创建用户单元.user.py。实现了flask_login (https://flask-login.readthedocs.io/en/latest/index.html#your-user-class)所提及的功能。以及用USERS字典暂时代替未来的数据库表。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask_login import UserMixin
from werkzeug.security import check_password_hash,generate_password_hash
USERS = [
{
"id":1,
"name":"admin",
"password":generate_password_hash('123')
},
{
"id":2,
"name":"李四",
"password":generate_password_hash('123')
},
]
class User(UserMixin):
def __init__(self,user):
self.username = user.get("name")
self.password_hash = user.get("password")
self.id = user.get("id")
@staticmethod
def queryUser(username):
for user in USERS:
if (user.get('name') == username) :
return User(user)
return None
def verifyPassword(self,password):
if self.password_hash is None:
return False
return check_password_hash(self.password_hash,password)
def get_id(self):
return self.id
def get(user_id):
if not user_id:
return None
for user in USERS:
if str(user.get('id')) == str(user_id) :
return User(user)
print('None')
return None
View Code
7.创建jwt操作单元jwt_token.py 。实现了对jwt的简单二次封装。其实不做封装也可以 7.1 JWT中 “Registered claims” 包含 iss(发行者),exp(到期时间),sub(主题),aud(受众)是官方建议携带的。我偷懒只采用了到期时间这一个声明。 7.2 jwt可以参考官网(https://jwt.io/)介于国内强大的长城。此官网偶发型能打开。 亦可参考此博文 https://www.cnblogs.com/mantoudev/p/8994341.html
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from jwt import jwt,PyJWTError
from datetime import datetime,timedelta
SECRECT_KEY = b'\x92R!\x8e\xc6\x9c\xb3\x89#\xa6\x0c\xcb\xf6\xcb\xd7\xbc'
def genToken(data):
expInt = datetime.utcnow() + timedelta(seconds=3)
payload = {
'exp': expInt,
'data': data
}
token = jwt.encode(payload,key= SECRECT_KEY,algorithm= 'HS256')
return bytes.decode(token)
def verfiyToken(tokenStr):
try:
tokenBytes = tokenStr.encode('utf-8')
payload = jwt.decode(tokenBytes,key= SECRECT_KEY,algorithm= 'HS256')
return payload
except PyJWTError as e:
print("jwt验证失败: %s" % e)
return None
View Code
8.创建登录逻辑单元 login.py 。同前端的主要交互逻辑都在此处。flask_login的具体实现(https://flask-login.readthedocs.io/en/latest/index.html#installation)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import time
import json
from flask import Blueprint,request
from flask_login import LoginManager,login_user,logout_user,login_required,current_user
from user import User,USERS
from jwt_token import genToken,verfiyToken
login_page = Blueprint('login_page',__name__)
login_manager = LoginManager()
login_manager.login_view = 'helloworld'
@login_page.record_once
def on_load(state):
login_manager.init_app(state.app)
# @login_manager.user_loader
# def load_user(user_id):
# return User.get(user_id)
@login_manager.request_loader
def load_user_from_request(request):
token = request.headers.get('Authorization')
if token == None:
return None
payload = verfiyToken(token)
if payload != None:
user = User.queryUser(payload['data']['username'])
else:
user = None
return user
@login_page.route('/first')
@login_required
def firstPage():
returnData = {'code': 0, 'msg': 'success', 'data': 'First Blood(来自' + current_user.username +')' }
return returnData,200
@login_page.route('/login', methods=['POST'])
def login():
if requesthod == 'POST':
username = request.form['username']
password = request.form['password']
user = User.queryUser(username)
if (user != None) and (user.verifyPassword(password)) :
login_user(user)
token = genToken({'username':username,'password':'******'})
returnData = {'code': 0, 'msg': 'success', 'data': {'token':token} }
return json.dumps(returnData),200
else :
returnData = {'code': 1, 'msg': 'success', 'data': 'username or password is not correct' }
return json.dumps(returnData),200
@login_page.route('/logout')
@login_required
def logout():
username = current_user.username
logout_user()
returnData = {'code': 0, 'msg': 'success', 'data': ' Bye ' + username }
return json.dumps(returnData),200
View Code
9.执行py app.py 后,在postman 分别测试如下链接(注意图中红框内容。): 9.1 http://127.0.0.1:5000/hello 9.2 http://127.0.0.1:5000/login 9.3 http://127.0.0.1:5000/first 9.4 http://127.0.0.1:5000/logout ![](https://img2020.cnblogs.com/blog/78238/202007/78238-20200718144518792-1420456524.png)
![](https://img2020.cnblogs.com/blog/78238/202007/78238-20200718144547916-80003660.png)
![](https://img2020.cnblogs.com/blog/78238/202007/78238-20200718144620556-1149800153.png)
![](https://img2020.cnblogs.com/blog/78238/202007/78238-20200718144640283-535004909.png)
10.后端代码初步结束。下一阶段,连接前后端
--------------------------------------------------------------------------------------------------------------------------------------------------
1.打开front-end项目,用axios把后端接口调用起来2.在项目src目录下 创建文件夹 utils 然后在其内创建文件request.js。这里对axios做了简单封装。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import axios from 'axios'
import Cookies from 'js-cookie'
/****** 创建axios实例 ******/
const service = axios.create({
baseURL: 'http://localhost:5000', // api的base_url
timeout: 5000 // 请求超时时间
})
service.interceptors.request.use(
config => {
config.headers['Authorization'] = Cookies.get('Authorization')
return config
},
error => {
console.log(error)
return Promise.reject(error)
}
)
/****** respone拦截器==>对响应做处理 ******/
// service.interceptors.response.use(
// response => {
// console.log(response)
// //这里根据后端提供的数据进行对应的处理
// if (response.data.result === 'TRUE') {
// return response.data;
// }
// },
// error => {
// console.log(error);
// return Promise.reject(error)
// }
// )
export default service;
request.js
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{{ titleMsg }}
提交
重置
测试
登出
import qs from 'qs'
import service from '../utils/request'
import Cookies from 'js-cookie'
export default {
name: 'loginForm',
data() {
return {
titleMsg: '欢迎来到旗帜世界',
loginData: {
username: '',
password: ''
}
}
},
methods: {
loginForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
service({url: '/login',method: 'post',data: qs.stringify(this.loginData)})
.then(response => {
const { data } = response
Cookies.set('Authorization',data.data.token)
alert('submit!!!' +'\n'+ data.msg)
})
.catch(error => {
console.log(error)
})
} else {
console.log('illegad submit!!');
return false;
}
})
},
testForm() {
service({url: '/first',method: 'get'})
.then(response => {
const { data } = response
alert('firstPage!!!' +'\n'+ data.data)
})
.catch(error => {
console.log(error)
})
},
logoutForm() {
service({url: '/logout',method: 'get'})
.then(response => {
const { data } = response
alert('logout!!!' +'\n'+ data.data)
})
.catch(error => {
console.log(error)
})
}
}
}
Login.vue
3.npm run serve 后,测试。四个按钮![](https://img2020.cnblogs.com/blog/78238/202007/78238-20200718152054078-1220661022.png)
4.其中关注一个状况,当登出后,再次点击测试。测试依然返回成功。这就出现一个问题,登出功能无效,回看后端代码logout是正常运作。稍加分析,即可得出产生这种情况的原因是jwt Token本身的弊端。前文的连接中已经提醒。如何解决此状况,日后再分析
5.收工了。
|